/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.cmd.component;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.engine.cmd.identity.LoginCmd;
import cn.easyplatform.engine.runtime.datalist.DataListUtils;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.interceptor.AbstractCommand;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.MoveRequestMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.vos.MoveResultVo;
import cn.easyplatform.messages.vos.MoveVo;
import cn.easyplatform.support.scripting.CompliableScriptEngine;
import cn.easyplatform.support.scripting.ScriptEngineFactory;
import cn.easyplatform.type.*;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class MoveCmd extends AbstractCommand<MoveRequestMessage> {

	private static final Logger log = LoggerFactory.getLogger(LoginCmd.class);

	/**
	 * @param req
	 */
	public MoveCmd(MoveRequestMessage req) {
		super(req);
	}

	@Override
	public IResponseMessage<?> execute(CommandContext cc) {
		MoveVo mv = req.getBody();
		WorkflowContext ctx = cc.getWorkflowContext();
		if(log.isDebugEnabled())
		log.debug("move type {}", mv.getType());
		if (req.getBody().getType() < 2) { // 添加
			ListContext lc = ctx.getList(mv.getTargetId());
			if (lc == null)// Listbox
				return addListbox(cc, ctx, mv);
			else
				return addDatalist(cc, ctx, lc, mv);
		} else {
			// 移除
			ListContext lc = ctx.getList(mv.getSourceId());
			if (lc == null)
				return removeListbox(cc, ctx, mv);
			else
				return removeDatalist(cc, ctx, lc, mv);
		}
	}

	/**
	 * 目标是listbox
	 * 
	 * @param ctx
	 * @param mv
	 * @return
	 */
	private IResponseMessage<?> addListbox(CommandContext cc,
			WorkflowContext ctx, MoveVo mv) {
		List<RecordContext> sourceData = getListData(cc, ctx, mv.getSourceId(),
				mv.getSourceData());
		List<Object[]> orginData = mv.getSourceData();
		List<MoveResultVo> result = new ArrayList<MoveResultVo>();
		// 先检查是否存在
		if (!Strings.isBlank(mv.getFilter())) {
			List<RecordContext> targetData = getListData(cc, ctx,
					mv.getTargetId(), mv.getTargetData());
			if (!targetData.isEmpty()) {
				CompliableScriptEngine engine = ScriptEngineFactory
						.createCompilableEngine(cc, mv.getFilter());
				try {
					for (int i = 0; i < sourceData.size(); i++) {
						RecordContext source = sourceData.get(i);
						for (int j = 0; j < targetData.size(); j++) {
							RecordContext target = targetData.get(j);
							Object b = engine.eval(source, target);
							if (b != null && (b instanceof Boolean)
									&& (Boolean) b) {// 更新现有的记录
								target.setParameter("759", "2");// 2表示从左到右->已存在
								if (!Strings.isBlank(mv.getMapping())) {
									String code = RuntimeUtils.eval(cc,
											mv.getMapping(), source, target);
									if (!code.equals("0000"))
										return MessageUtils.byErrorCode(cc,
												target, ctx.getId(), code);
								}
								target.setParameter("815", false);
								FieldVo[] fvs = (FieldVo[]) mv.getTargetData()
										.get(j);
								for (FieldVo fv : fvs)
									fv.setValue(target.getValue(fv.getName()));
								result.add(new MoveResultVo(orginData.get(i),
										j, fvs));
								sourceData.set(i, null);
								break;
							}
						}
					}
				} finally {
					engine.destroy();
				}
			}
		}
		for (int i = 0; i < sourceData.size(); i++) {
			RecordContext source = sourceData.get(i);
			if (source != null) {
				FieldDo[] fvs = getMetaData(cc, ctx, mv.getListboxQuery(),
						mv.getTargetData());
				RecordContext target = ctx.getRecord().create();
				for (FieldDo fv : fvs)
					target.getData().set(fv);
				target.setParameter("759", "1");// 1表示从左到右->新增
				// 再执行移动逻辑
				if (!Strings.isBlank(mv.getMapping())) {
					String code = RuntimeUtils.eval(cc, mv.getMapping(),
							source, target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				FieldVo[] data = new FieldVo[fvs.length];
				for (int j = 0; j < data.length; j++)
					data[j] = RuntimeUtils.castTo(fvs[j]);
				// 最后生成列表记录
				result.add(new MoveResultVo(orginData.get(i), data));
			}
		}
		return new SimpleResponseMessage(result);
	}

	/**
	 * 目标是datalist
	 * 
	 * @param cc
	 * @param ctx
	 * @param lc
	 * @param mv
	 * @return
	 */
	private IResponseMessage<?> addDatalist(CommandContext cc,
			WorkflowContext ctx, ListContext lc, MoveVo mv) {
		List<RecordContext> sourceData = getListData(cc, ctx, mv.getSourceId(),
				mv.getSourceData());
		List<MoveResultVo> result = new ArrayList<MoveResultVo>();
		List<Object[]> orginData = mv.getSourceData();
		// 先检查是否存在
		if (!Strings.isBlank(mv.getFilter())) {
			List<RecordContext> targetData = null;
			if (mv.getTargetData() == null)
				targetData = lc.getRecords();
			else {
				targetData = new ArrayList<RecordContext>();
				for (Object[] obj : mv.getTargetData()) {
					RecordContext rc = null;
					if (obj instanceof FieldVo[]) {// 来自listbox
						FieldVo[] fields = (FieldVo[]) obj;
						rc = ctx.getRecord().clone();
						for (FieldVo field : fields)
							rc.setVariable(RuntimeUtils.castTo(field));
					} else {// 普通列表
						rc = lc.getRecord(obj);
						if (rc == null) {
							Record record = DataListUtils
									.getRecord(cc, lc, obj);
							if (lc.isCustom())
								rc = lc.createRecord(obj, record);
							else
								rc = lc.createRecord(record);
							rc.setParameter("815", false);
							rc.setParameter("814", "R");
							// lc.appendRecord(rc, false);
						}
					}
					targetData.add(rc);
				}
			}
			if (!targetData.isEmpty()) {
				CompliableScriptEngine engine = ScriptEngineFactory
						.createCompilableEngine(cc, mv.getFilter());
				try {
					for (int i = 0; i < sourceData.size(); i++) {
						RecordContext source = sourceData.get(i);
						for (RecordContext target : targetData) {
							Object b = engine.eval(source, target);
							if (b != null && (b instanceof Boolean)
									&& (Boolean) b) {// 更新现有的记录
								target.setParameter("759", "2");// 2表示从左到右->已存在
								if (!Strings.isBlank(mv.getMapping())) {
									String code = RuntimeUtils.eval(cc,
											mv.getMapping(), source, target);
									if (!code.equals("0000"))
										return MessageUtils.byErrorCode(cc,
												target, ctx.getId(), code);
								}
								char code = target.getParameterAsChar("814");
								if (code != 'C')
									target.setParameter("814", "U");
								target.setParameter("815", true);
								result.add(new MoveResultVo(
										orginData.get(i),
										code == 'D' ? -1 : 0,
										new ListRowVo(
												target.getKeyValues(),
												DataListUtils.wrapRow(cc, lc,
														target),
												target.getParameterAsBoolean("853"))));
								sourceData.set(i, null);
								break;
							}
						}
					}
				} finally {
					engine.destroy();
				}
			}
		}
		for (int i = 0; i < sourceData.size(); i++) {
			RecordContext source = sourceData.get(i);
			if (source != null) {
				RecordContext target = null;
				if (lc.isCustom())
					target = lc.createRecord((Object[]) null);
				else {
					TableBean tb = cc.getEntity(lc.getBean().getTable());
					if (tb == null)
						return MessageUtils.entityNotFound(EntityType.TABLE
								.getName(), lc.getBean().getTable());
					target = lc.createRecord(RuntimeUtils.createRecord(cc, tb,
							tb.isAutoKey()));
					target.setParameter("814", "C");
					target.setParameter("815", true);
				}
				target.setParameter("759", "1");// 1表示从左到右->新增
				// 先初始化原列表的逻辑
				if (!Strings.isBlank(lc.getBeforeLogic())) {
					String code = RuntimeUtils.eval(cc, lc.getBeforeLogic(),
							ctx.getRecord(), target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				// 再执行移动逻辑
				if (!Strings.isBlank(mv.getMapping())) {
					String code = RuntimeUtils.eval(cc, mv.getMapping(),
							source, target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				Object[] data = DataListUtils.wrapRow(cc, lc, target);
				if (lc.isCustom())
					target.setKeyValues(data);
				lc.appendRecord(target, false);
				MoveResultVo meta = new MoveResultVo(orginData.get(i), new ListRowVo(
						target.getKeyValues(), data, target
						.getParameterAsBoolean("853")));
				if (mv.isUnique()) {
					ListContext scl = ctx.getList(mv.getSourceId());
					if (scl != null){// && source.getParameterAsChar("814") == 'C'){
						scl.removeRecord(source);
						meta.setDropSource(true);
					}
				}
				// 最后生成列表记录
				result.add(meta);
			}
		}
		return new SimpleResponseMessage(result);
	}

	private IResponseMessage<?> removeListbox(CommandContext cc,
			WorkflowContext ctx, MoveVo mv) {
		List<RecordContext> sourceData = getListData(cc, ctx, mv.getTargetId(),
				mv.getTargetData());
		List<Object[]> orginData = mv.getTargetData();
		List<MoveResultVo> result = new ArrayList<MoveResultVo>();
		// 先检查是否存在
		if (!Strings.isBlank(mv.getFilter())) {
			List<RecordContext> targetData = getListData(cc, ctx,
					mv.getSourceId(), mv.getSourceData());
			if (!targetData.isEmpty()) {
				CompliableScriptEngine engine = ScriptEngineFactory
						.createCompilableEngine(cc, mv.getFilter());
				try {
					for (int i = 0; i < sourceData.size(); i++) {
						RecordContext source = sourceData.get(i);
						for (int j = 0; j < targetData.size(); j++) {
							RecordContext target = targetData.get(j);
							Object b = engine.eval(source, target);
							if (b != null && (b instanceof Boolean)
									&& (Boolean) b) {// 更新现有的记录
								target.setParameter("759", "4");// 4表示从右到左->已存在
								if (!Strings.isBlank(mv.getMapping())) {
									String code = RuntimeUtils.eval(cc,
											mv.getMapping(), source, target);
									if (!code.equals("0000"))
										return MessageUtils.byErrorCode(cc,
												target, ctx.getId(), code);
								}
								target.setParameter("815", false);
								FieldVo[] fvs = (FieldVo[]) mv.getTargetData()
										.get(j);
								for (FieldVo fv : fvs)
									fv.setValue(target.getValue(fv.getName()));
								result.add(new MoveResultVo(orginData.get(i),
										j, fvs));
								sourceData.set(i, null);
								break;
							}
						}
					}
				} finally {
					engine.destroy();
				}
			}
		}
		for (int i = 0; i < sourceData.size(); i++) {
			RecordContext source = sourceData.get(i);
			if (source != null) {
				FieldDo[] fvs = getMetaData(cc, ctx, mv.getListboxQuery(),
						mv.getSourceData());
				RecordContext target = ctx.getRecord().create();
				for (FieldDo fv : fvs)
					target.getData().set(fv);
				target.setParameter("759", "3");// 3表示从右到左->新增
				// 再执行移动逻辑
				if (!Strings.isBlank(mv.getMapping())) {
					String code = RuntimeUtils.eval(cc, mv.getMapping(),
							source, target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				FieldVo[] data = new FieldVo[fvs.length];
				for (int j = 0; j < data.length; j++)
					data[j] = RuntimeUtils.castTo(fvs[j]);
				// 最后生成列表记录
				result.add(new MoveResultVo(orginData.get(i), data));
			}
		}
		return new SimpleResponseMessage(result);
	}

	private IResponseMessage<?> removeDatalist(CommandContext cc,
			WorkflowContext ctx, ListContext lc, MoveVo mv) {
		List<RecordContext> sourceData = getListData(cc, ctx, mv.getTargetId(),
				mv.getTargetData());
		List<MoveResultVo> result = new ArrayList<MoveResultVo>();
		List<Object[]> orginData = mv.getTargetData();
		// 先检查是否存在
		if (!Strings.isBlank(mv.getFilter())) {
			List<RecordContext> targetData = null;
			if (mv.getSourceData() == null)
				targetData = lc.getRecords();
			else {
				targetData = new ArrayList<RecordContext>();
				for (Object[] obj : mv.getSourceData()) {
					RecordContext rc = null;
					if (obj instanceof FieldVo[]) {// 来自listbox
						FieldVo[] fields = (FieldVo[]) obj;
						rc = ctx.getRecord().clone();
						for (FieldVo field : fields)
							rc.setVariable(RuntimeUtils.castTo(field));
					} else {// 普通列表
						rc = lc.getRecord(obj);
						if (rc == null) {
							Record record = DataListUtils
									.getRecord(cc, lc, obj);
							if (lc.isCustom())
								rc = lc.createRecord(obj, record);
							else
								rc = lc.createRecord(record);
							rc.setParameter("815", false);
							rc.setParameter("814", "R");
							// lc.appendRecord(rc, false);
						}
					}
					targetData.add(rc);
				}
			}
			if (!targetData.isEmpty()) {
				CompliableScriptEngine engine = ScriptEngineFactory
						.createCompilableEngine(cc, mv.getFilter());
				try {
					for (int i = 0; i < sourceData.size(); i++) {
						RecordContext source = sourceData.get(i);
						for (RecordContext target : targetData) {
							Object b = engine.eval(source, target);
							if (b != null && (b instanceof Boolean)
									&& (Boolean) b) {// 更新现有的记录
								target.setParameter("759", "4");// 4表示从右到左->已存在
								if (!Strings.isBlank(mv.getMapping())) {
									String code = RuntimeUtils.eval(cc,
											mv.getMapping(), source, target);
									if (!code.equals("0000"))
										return MessageUtils.byErrorCode(cc,
												target, ctx.getId(), code);
								}
								MoveResultVo meta = new MoveResultVo(
										orginData.get(i),
										0,
										new ListRowVo(
												target.getKeyValues(),
												DataListUtils.wrapRow(cc, lc,
														target),
												target.getParameterAsBoolean("853")));
								if (mv.isUnique()) {
									ListContext scl = ctx.getList(mv
											.getTargetId());
									if (scl != null) {
										char code = source
												.getParameterAsChar("814");
										if (code == 'C') {
											scl.removeRecord(source);
											meta.setDropSource(true);
										} else {
											source.setParameter("814", "D");
											if (!scl.isCustom())
												source.setParameter("815", true);
										}
									}
								}
								result.add(meta);
								sourceData.set(i, null);
								break;
							}
						}
					}
				} finally {
					engine.destroy();
				}
			}
		}
		for (int i = 0; i < sourceData.size(); i++) {
			RecordContext source = sourceData.get(i);
			if (source != null) {
				RecordContext target = null;
				if (lc.isCustom())
					target = lc.createRecord((Object[]) null);
				else {
					TableBean tb = cc.getEntity(lc.getBean().getTable());
					if (tb == null)
						return MessageUtils.entityNotFound(EntityType.TABLE
								.getName(), lc.getBean().getTable());
					target = lc.createRecord(RuntimeUtils.createRecord(cc, tb,
							tb.isAutoKey()));
					target.setParameter("814", "C");
					target.setParameter("815", false);
				}
				target.setParameter("759", "3");// 3表示从右到左->新增
				// 先初始化原列表的逻辑
				if (!Strings.isBlank(lc.getBeforeLogic())) {
					String code = RuntimeUtils.eval(cc, lc.getBeforeLogic(),
							ctx.getRecord(), target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				// 再执行移动逻辑
				if (!Strings.isBlank(mv.getMapping())) {
					String code = RuntimeUtils.eval(cc, mv.getMapping(),
							source, target);
					if (!code.equals("0000"))
						return MessageUtils.byErrorCode(cc, target,
								ctx.getId(), code);
				}
				Object[] data = DataListUtils.wrapRow(cc, lc, target);
				if (lc.isCustom())
					target.setKeyValues(data);
				lc.appendRecord(target, false);
				MoveResultVo meta = new MoveResultVo(orginData.get(i),
						new ListRowVo(target.getKeyValues(), data,
								target.getParameterAsBoolean("853")));
				if (mv.isUnique()) {
					ListContext scl = ctx.getList(mv.getTargetId());
					if (scl != null) {
						if (source.getParameterAsChar("814") == 'C') {
							scl.removeRecord(source);
							meta.setDropSource(true);
						} else {
							source.setParameter("814", "D");
							if (!scl.isCustom())
								source.setParameter("815", true);
						}
					}
				}
				// 最后生成列表记录
				result.add(meta);
			}
		}
		return new SimpleResponseMessage(result);
	}

	private FieldDo[] getMetaData(CommandContext cc, WorkflowContext ctx,
			String query, List<Object[]> data) {
		FieldDo[] fvs = null;
		if (data.isEmpty()) {
			int pos = query.toLowerCase().indexOf("where");
			if (pos > 0)
				query = query.substring(pos);
			query += " WHERE 1=0";
			fvs = cc.getBizDao().getMetaData(query);
		} else {
			FieldVo[] src = (FieldVo[]) data.get(0);
			fvs = new FieldDo[src.length];
			for (int i = 0; i < src.length; i++) {
				fvs[i] = new FieldDo(src[i].getType());
				fvs[i].setName(src[i].getName());
				fvs[i].setDecimal(src[i].getDecimal());
				fvs[i].setLength(src[i].getLength());
			}
		}
		return fvs;
	}

	private List<RecordContext> getListData(CommandContext cc,
			WorkflowContext ctx, String sourceId, List<Object[]> data) {
		List<RecordContext> result = new ArrayList<RecordContext>();
		for (Object[] sourceData : data) {
			if (sourceData instanceof FieldVo[]) {// 从listbox
				RecordContext rc = ctx.getRecord().create();
				for (int i = 0; i < sourceData.length; i++)
					rc.getData().set(
							RuntimeUtils.castTo((FieldVo) sourceData[i]));
				result.add(rc);
			} else {
				ListContext lc = ctx.getList(sourceId);
				if (lc == null)
					throw new EasyPlatformWithLabelKeyException(
							"datalist.not.found", sourceId);
				if (lc.getType().equals(Constants.DETAIL))
					result.add(lc.getRecord(sourceData));
				else {
					RecordContext rc = lc.getRecord(sourceData);
					if (rc == null) {
						Record record = DataListUtils.getRecord(cc, lc,
								sourceData);
						if (lc.isCustom())
							rc = lc.createRecord(sourceData, record);
						else
							rc = lc.createRecord(record);
						rc.setParameter("815", false);
						rc.setParameter("814", "R");
						lc.appendRecord(rc, false);
					}
					result.add(rc);
				}
			}
		}
		return result;
	}
}
